/**
 * Title : jFamilyTree browser
 * Description: jFamilyTree browser
 * About: Designed and rewritten by Andreas Lundgren, based on the Treebolic application suite
 * Copyright:	(c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.browser;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

import jFamilyTree.IWidget;
import jFamilyTree.Widget;
import jFamilyTree.commons.ButtonTabComponent;
import jFamilyTree.commons.FileDialogs;
import jFamilyTree.commons.Help;
import jFamilyTree.commons.OpenDialog;
import jFamilyTree.commons.Persist;
import jFamilyTree.commons.SettingsDialog;
import jFamilyTree.commons.UrlDialog;

/**
 * Browser main frame
 * 
 * @author Bernard Bou
 */
public class MainFrame extends JFrame implements HyperlinkListener
{
	private static final long serialVersionUID = 1L;

	// D A T A

	/**
	 * Command line arguments
	 */
	private String[] theArgs;

	/**
	 * Persist
	 */
	private Properties theSettings = new Properties();

	// C O M P O N E N T S

	/**
	 * Tabbed pane
	 */
	private JTabbedPane theTabbedPane;

	// C O N S T R U C T O R

	/**
	 * Constructor
	 * 
	 * @param theseArgs
	 *        command line arguments
	 */
	public MainFrame(String[] theseArgs)
	{
		// System.out.println("CLASSPATH=<" + System.getProperty("java.class.path", ".") + ">");

		// settings
		theArgs = theseArgs;
		theSettings = Persist.getSettings("jFamilyTree-browser"); //$NON-NLS-1$
		setTitle(Messages.getString("MainFrame.0")); //$NON-NLS-1$

		// menu
		setJMenuBar(makeMenuBar());

		// components
		Container thisContentPane = getContentPane();
		thisContentPane.setLayout(new BorderLayout());
		JEditorPane thisHome = makeHome();
		thisHome.setPreferredSize(new Dimension(600, 620));
		thisContentPane.add(thisHome);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		pack();
		setVisible(true);

		open();
	}

	/*
	 * (non-Javadoc)
	 * @see java.awt.Window#processWindowEvent(java.awt.event.WindowEvent)
	 */
	protected void processWindowEvent(WindowEvent thisEvent)
	{
		if (thisEvent.getID() == WindowEvent.WINDOW_CLOSING)
			Persist.saveSettings("jFamilyTree-browser", theSettings); //$NON-NLS-1$
		super.processWindowEvent(thisEvent);
	}

	// M A K E . C O M P O N E N T S

	/**
	 * HTML home pane
	 */
	private JEditorPane makeHome()
	{
		return makeBrowserPane(MainFrame.class.getResource("doc/home.html")); //$NON-NLS-1$
	}

	/**
	 * HTML pane
	 */
	private JEditorPane makeBrowserPane(URL thisUrl)
	{
		JEditorPane thisPanel = new JEditorPane();
		thisPanel.setEditable(false);
		try
		{
			thisPanel.setPage(thisUrl);
		}
		catch (IOException e)
		{
			thisPanel.setText(Messages.getString("MainFrame.101") + thisUrl); //$NON-NLS-1$
		}
		thisPanel.addHyperlinkListener(this);
		return thisPanel;
	}

	/**
	 * Make menu bar
	 * 
	 * @return menu bar
	 */
	private JMenuBar makeMenuBar()
	{
		JMenuItem thisOpenMenuItem = makeItem(Messages.getString("MainFrame.11"), "open.png", Code.OPEN, 79); //$NON-NLS-1$ //$NON-NLS-2$
		JMenuItem thisOpenHttpMenuItem = makeItem(Messages.getString("MainFrame.12"), "openurl.png", Code.OPENURL, 0); //$NON-NLS-1$ //$NON-NLS-2$
		JMenuItem thisOpenParseMenuItem = makeItem(Messages.getString("MainFrame.13"), "openprovider.png", Code.OPENPROVIDER, 0); //$NON-NLS-1$ //$NON-NLS-2$

		JMenuItem thisSettingsMenuItem = makeItem(Messages.getString("MainFrame.21"), "settings.png", Code.SETTINGS, 0); //$NON-NLS-1$ //$NON-NLS-2$

		JMenuItem thisAboutMenuItem = makeItem(Messages.getString("MainFrame.31"), "about.png", Code.ABOUT, 0); //$NON-NLS-1$ //$NON-NLS-2$
		JMenuItem thisHelpMenuItem = makeItem(Messages.getString("MainFrame.32"), "help.png", Code.HELP, 0); //$NON-NLS-1$ //$NON-NLS-2$

		JMenu thisFilesMenu = new JMenu();
		thisFilesMenu.setText(Messages.getString("MainFrame.10")); //$NON-NLS-1$
		thisFilesMenu.add(thisOpenMenuItem);
		thisFilesMenu.add(thisOpenHttpMenuItem);
		thisFilesMenu.add(thisOpenParseMenuItem);

		JMenu thisHelpMenu = new JMenu();
		thisHelpMenu.setText(Messages.getString("MainFrame.30")); //$NON-NLS-1$
		thisHelpMenu.add(thisAboutMenuItem);
		thisHelpMenu.add(thisHelpMenuItem);

		JMenu thisOptionsMenu = new JMenu();
		thisOptionsMenu.setText(Messages.getString("MainFrame.20")); //$NON-NLS-1$
		thisOptionsMenu.add(thisSettingsMenuItem);

		JMenuBar thisMenuBar = new JMenuBar();
		thisMenuBar.add(thisFilesMenu);
		thisMenuBar.add(thisOptionsMenu);
		thisMenuBar.add(thisHelpMenu);
		return thisMenuBar;
	}

	/**
	 * Make menu item
	 * 
	 * @param thisText
	 *        text
	 * @param thisImage
	 *        image
	 * @param thisCommand
	 *        command code
	 * @param thisAcceleratorKey
	 *        accelerator key
	 * @return menu item
	 */
	private JMenuItem makeItem(String thisText, String thisImage, final Code thisCommand, int thisAcceleratorKey)
	{
		JMenuItem thisItem = new JMenuItem();
		thisItem.setText(thisText);
		if (thisImage != null)
			thisItem.setIcon(new ImageIcon(MainFrame.class.getResource("images/" + thisImage))); //$NON-NLS-1$
		if (thisAcceleratorKey != 0)
			thisItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(thisAcceleratorKey, java.awt.event.KeyEvent.CTRL_MASK, false));
		thisItem.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				execute(thisCommand);
			}
		});
		return thisItem;
	}

	// C O M M A N D

	/**
	 * Command codes
	 */
	static private enum Code
	{
		OPEN, OPENURL, OPENPROVIDER, SETTINGS, ABOUT, HELP
	};

	/**
	 * Run command
	 * 
	 * @param thisCode
	 *        command code
	 */
	private void execute(Code thisCode)
	{
		switch (thisCode)
		{
		case OPEN:
		{
			String thisSource = FileDialogs.getXmlUrl(theSettings.getProperty("base", ".")); //$NON-NLS-1$ //$NON-NLS-2$
			if (thisSource != null)
				open(null, thisSource, null, null);
			break;
		}
		case OPENURL:
		{
			JDialog thisDialog = new UrlDialog(theSettings);
			thisDialog.setModal(true);
			thisDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			thisDialog.setVisible(true);
			String thisSource = theSettings.getProperty("openurl"); //$NON-NLS-1$
			if (thisSource != null)
				open(null, thisSource, null, null);
			break;
		}
		case OPENPROVIDER:
		{
			JDialog thisDialog = new OpenDialog(theSettings);
			thisDialog.setModal(true);
			thisDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			thisDialog.setVisible(true);
			String thisSource = theSettings.getProperty("source"); //$NON-NLS-1$
			String thisProvider = theSettings.getProperty("provider"); //$NON-NLS-1$
			if (thisSource != null && !thisSource.isEmpty() && thisProvider != null && !thisProvider.isEmpty())
				open(thisProvider, thisSource, null, null);
			break;
		}
		case ABOUT:
		{
			JDialog thisDialog = new AboutDialog();
			thisDialog.setModal(true);
			thisDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			thisDialog.setVisible(true);
			break;
		}
		case SETTINGS:
		{
			SettingsDialog thisDialog = new SettingsDialog(theSettings);
			thisDialog.setModal(true);
			thisDialog.setVisible(true);
			thisDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			if (thisDialog.ok)
				Persist.saveSettings("jFamilyTree-browser", theSettings); //$NON-NLS-1$
			break;
		}
		case HELP:
		{
			help();
			break;
		}
		}
	}

	/**
	 * Open
	 */
	private void open()
	{
		List<ContextData> theseContextData = getContextData();
		if (theseContextData != null)
			for (final ContextData thisContextData : theseContextData)
			{
				SwingUtilities.invokeLater(new Runnable()
				{
					public void run()
					{
						open(thisContextData.theProvider, thisContextData.theSource, thisContextData.theBase, thisContextData.theImageBase);
					}
				});
			}
	}

	/**
	 * Open source
	 * 
	 * @param thisSource
	 *        source
	 */
	private void open(String thisProvider, String thisSource, String thisBase, String thisImageBase)
	{
		// widget
		Context thisContext = new Context(this, thisSource, thisBase, thisImageBase);
		IWidget thisWidget = new Widget(thisContext);
		thisContext.connect(thisWidget);

		if (thisProvider != null && thisProvider.isEmpty())
			thisProvider = null;

		thisWidget.init(thisProvider, thisSource);

		// tabbed pane
		if (theTabbedPane == null)
		{
			theTabbedPane = new JTabbedPane();
			Container thisContentPane = getContentPane();
			thisContentPane.removeAll();
			thisContentPane.add(theTabbedPane);
			validate();
		}

		// tab
		String thisToolTip = "<html><body><strong>" + thisSource + "</strong><br>" + thisProvider + "</body></html>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		theTabbedPane.addTab(mangle(thisSource), null, (Component) thisWidget, thisToolTip);
		int thisIndex = theTabbedPane.indexOfComponent((Component) thisWidget);
		theTabbedPane.setTabComponentAt(thisIndex, new ButtonTabComponent(theTabbedPane));
		theTabbedPane.setSelectedIndex(thisIndex);
	}

	/**
	 * Mangle string
	 * 
	 * @param thisString
	 *        string to mangle
	 * @return mangled string
	 */
	private String mangle(String thisString)
	{
		int thisIndex = Math.max(thisString.lastIndexOf('/'), thisString.lastIndexOf('\\'));
		if (thisIndex != -1)
			thisString = thisString.substring(thisIndex + 1);
		return thisString;
	}

	/**
	 * Help
	 */
	private void help()
	{
		Help.help(theSettings.getProperty("browser"), theSettings.getProperty("help")); //$NON-NLS-1$ //$NON-NLS-2$
	}

	// A R G U M E N T S

	/**
	 * Get parameter
	 * 
	 * @param thisName
	 *        parameter
	 * @return parameter value
	 */
	public String getParameter(String thisName)
	{
		// param1=<val> param2=<"val with spaces"> ...
		if (theArgs == null)
			return null;

		for (String thisArg : theArgs)
			if (thisArg.startsWith(thisName + "=")) //$NON-NLS-1$
			{
				String thisValue = thisArg.substring(thisName.length() + 1);
				if (thisValue.startsWith("\"")) //$NON-NLS-1$
				{
					thisValue = thisValue.substring(1);
					if (thisValue.endsWith("\"")) //$NON-NLS-1$
						thisValue = thisValue.substring(0, thisValue.length() - 1);
				}
				return thisValue;
			}
		return null;
	}

	/**
	 * Context data
	 */
	private class ContextData
	{
		/**
		 * Provider
		 */
		public String theProvider;

		/**
		 * Source
		 */
		public String theSource;

		/**
		 * Document base
		 */
		public String theBase;

		/**
		 * Image base
		 */
		public String theImageBase;

		/**
		 * Constructor
		 * 
		 * @param thisProvider
		 *        provider class
		 * @param thisSource
		 *        document source
		 * @param thisBase
		 *        document base
		 * @param thisImageBase
		 *        image base
		 */
		public ContextData(String thisProvider, String thisSource, String thisBase, String thisImageBase)
		{
			theProvider = thisProvider;
			theSource = thisSource;
			theBase = thisBase;
			theImageBase = thisImageBase;
		}

		/*
		 * (non-Javadoc)
		 * @see java.lang.Object#toString()
		 */
		public String toString()
		{
			return "p=" + theProvider + " s=" + theSource + " b=" + theBase + " i=" + theImageBase; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		}
	}

	/**
	 * Parse context data from command line
	 * 
	 * @return context data from command line
	 */
	private List<ContextData> getContextData()
	{
		if (theArgs == null)
			return null;
		boolean debug = false;
		String thisDefaultProvider = null;
		String thisDefaultBase = null;
		String thisDefaultImageBase = null;
		String thisProvider = null;
		String thisBase = null;
		String thisImageBase = null;
		List<ContextData> theseSources = null;
		for (String thisArg : theArgs)
		{
			if (thisArg.startsWith("base=")) //$NON-NLS-1$
			{
				thisDefaultBase = thisArg.substring(5);
				continue;
			}
			if (thisArg.startsWith("images=")) //$NON-NLS-1$
			{
				thisDefaultImageBase = thisArg.substring(7);
				continue;
			}
			if (thisArg.startsWith("provider=")) //$NON-NLS-1$
			{
				thisDefaultProvider = thisArg.substring(9);
				continue;
			}
			if (thisArg.startsWith("debug=")) //$NON-NLS-1$
			{
				debug = true;
				continue;
			}
			if (thisArg.startsWith("provider1=")) //$NON-NLS-1$
			{
				thisProvider = thisArg.substring(10);
				continue;
			}
			if (thisArg.startsWith("base1=")) //$NON-NLS-1$
			{
				thisBase = thisArg.substring(6);
				continue;
			}
			if (thisArg.startsWith("images1=")) //$NON-NLS-1$
			{
				thisImageBase = thisArg.substring(8);
				continue;
			}

			String[] theseFields = thisArg.split("="); //$NON-NLS-1$
			String thisSource = theseFields[0];
			if (theseFields.length > 1)
				thisSource = theseFields[1];

			if (theseSources == null)
				theseSources = new ArrayList<ContextData>();
			ContextData thisContext = new ContextData(thisProvider != null ? thisProvider : thisDefaultProvider, thisSource, thisBase != null ? thisBase : thisDefaultBase, thisImageBase != null ? thisImageBase : thisDefaultImageBase);
			theseSources.add(thisContext);
			if (debug)
				System.out.println("CONTEXT=" + thisContext);

			// clear
			thisProvider = null;
			thisBase = null;
			thisImageBase = null;
		}
		return theseSources;
	}

	// E V E N T

	/**
	 * Link to
	 * 
	 * @param thisLinkUrl
	 *        link url
	 * @param thisLinkTarget
	 *        thisLinkTarget link target
	 * @param thisWidget
	 *        widget
	 * @param thisContext
	 *        widget's context
	 */
	public void linkTo(String thisLinkUrl, String thisLinkTarget, IWidget thisWidget, Context thisContext)
	{
		int thisIndex = theTabbedPane.indexOfComponent((Component) thisWidget);
		URL thisUrl = thisContext.makeURL(thisLinkUrl);
		if (thisUrl == null)
			return;

		JComponent thisComponent = makeBrowserPane(thisUrl);

		theTabbedPane.setComponentAt(thisIndex, thisComponent);
		theTabbedPane.setTitleAt(thisIndex, mangle(thisLinkUrl));
		theTabbedPane.setToolTipTextAt(thisIndex, thisLinkUrl);
	}

	/*
	 * (non-Javadoc)
	 * @see javax.swing.event.HyperlinkListener#hyperlinkUpdate(javax.swing.event.HyperlinkEvent)
	 */
	public void hyperlinkUpdate(HyperlinkEvent thisEvent)
	{
		// follow link
		JEditorPane thisEditorPane = (JEditorPane) thisEvent.getSource();
		try
		{
			thisEditorPane.setPage(thisEvent.getURL());
		}
		catch (IOException e)
		{
			thisEditorPane.setText(Messages.getString("MainFrame.103") + thisEvent.getURL()); //$NON-NLS-1$
		}
	}
}
